/**
 * @file      wiota_freq.c
 * @brief
 * @version   1.0
 * @date      2023.12.14
 *
 * @copyright Copyright (c) 2018 UCchip Technology Co.,Ltd. All rights reserved.
 *
 * Change Logs:
 * Date          Author       Notes
 * 2023.12.14    ucchip       the first version
 *
 */

#include "wiota_freq.h"
#include "config_api.h"
#include "uc_wiota_api.h"
#include "watchdog_app.h"
#include <rtdevice.h>
#include <rtthread.h>
#define DBG_ENABLE
#define DBG_SECTION_NAME "freq"
#include "uc_log.h"

// 扫频超时时间，足够大保证扫频不会超时
#define WIOTA_SCAN_TIMEOUT (2 * 60 * 1000)
// 扫频失败最大次数，达到最大次数后重启网关
#define MAX_SACN_FAIL_CNT (10)

/**
 * @brief 频点信息节点
 *
 */
typedef struct
{
    rt_list_t node;
    uc_scan_freq_t freq_info;
} freq_info_node_t;

/**
 * @brief 频点管理信息结构体
 *
 */
typedef struct
{
    freq_info_node_t freq_list;      /**< 频点管理链表 */
    freq_info_node_t *cur_freq_node; /**< 扫频完成后将该指针指向频点管理链表中的第一个节点，作为WIoTa的频点 */
    unsigned int continue_scan_fail; /**< 扫频失败次数，成功后归零 */
} freq_manager_info_t;

// 频点管理信息
freq_manager_info_t g_freq_mng_info = {0};

/**
 * @brief 频点管理信息初始化，频点链表初始化
 *
 */
static void wiota_freq_list_init(void)
{
    rt_memset(&g_freq_mng_info, 0, sizeof(freq_manager_info_t));
    rt_list_init(&g_freq_mng_info.freq_list.node);
}

/**
 * @brief 输出频点列表中的所有频点信息
 *
 */
static void wiota_print_freq_list(void)
{
    freq_info_node_t *temp_node;

    rt_list_for_each_entry(temp_node, &g_freq_mng_info.freq_list.node, node)
    {
        TRACE_I("address 0x%x freq_idx %d snr %d rssi %d is_synced %d",
                temp_node, temp_node->freq_info.freq_idx, temp_node->freq_info.snr, temp_node->freq_info.rssi, temp_node->freq_info.is_synced);
    }
}

/**
 * @brief 从静态数据中获取频点列表，当碰到0xff时停止，并返回有效频点个数
 *
 * @param freq_list 频点列表
 * @return freq_num 有效频点个数
 */
int wiota_get_freq_list(unsigned char *freq_list)
{
    int freq_num = 0;
    wiota_freq_cfg_t *freq_cfg = uc_static_get_wiota_freq_cfg();

    for (int i = 0; i < UC_FREQ_LIST_MAX_NUM; i++)
    {
        if (freq_cfg->freq_list[i] == 0xFF)
        {
            break;
        }
        if (freq_cfg->freq_list[i] <= 200 && freq_cfg->freq_list[i] >= 0)
        {
            freq_list[i] = freq_cfg->freq_list[i];
            freq_num++;
            TRACE_I("static freq_num %d freq_list[%d] %d", freq_num, i, freq_list[i]);
        }
    }

    return freq_num;
}

/**
 * @brief 从频点链表中查找某个频点是否存在，如果存在则返回该节点，不存在则返回RT_NULL
 *
 * @param freq_idx 频点
 * @return 频点节点
 */
static freq_info_node_t *wiota_freq_find_node(unsigned char freq_idx)
{
    freq_info_node_t *temp_node;

    rt_list_for_each_entry(temp_node, &g_freq_mng_info.freq_list.node, node)
    {
        if (temp_node->freq_info.freq_idx == freq_idx)
        {
            return temp_node;
        }
    }

    return RT_NULL;
}

/**
 * @brief 将一个频点的信息保存到频点节点中，并将节点添加到频点管理链表中，并在插入链表时按照rssi排序
 *
 * @param freq_info 频点信息
 */
static void wiota_add_freq_to_list(uc_scan_freq_t *freq_info)
{
    freq_info_node_t *temp_node = wiota_freq_find_node(freq_info->freq_idx);
    if (RT_NULL != temp_node)
    {
        temp_node->freq_info.snr = freq_info->snr;
        temp_node->freq_info.rssi = freq_info->rssi;
        temp_node->freq_info.is_synced = freq_info->is_synced;
    }
    else
    {
        freq_info_node_t *new_node = rt_malloc(sizeof(freq_info_node_t));
        if (new_node == RT_NULL)
        {
            TRACE_E("%s line %d malloc error", __FUNCTION__, __LINE__);
            return;
        }
        new_node->freq_info.freq_idx = freq_info->freq_idx;
        new_node->freq_info.is_synced = freq_info->is_synced;
        new_node->freq_info.rssi = freq_info->rssi;
        new_node->freq_info.snr = freq_info->snr;

        freq_info_node_t *temp_node1;

        rt_list_for_each_entry(temp_node1, &g_freq_mng_info.freq_list.node, node)
        {
            if (temp_node1->freq_info.rssi > new_node->freq_info.rssi)
            {
                rt_list_insert_before(&temp_node1->node, &new_node->node);
                return;
            }
        }

        rt_list_insert_after(g_freq_mng_info.freq_list.node.prev, &new_node->node);
    }
}

/**
 * @brief 从频点管理链表中删除某个节点，释放节点资源
 *
 * @param freq_info 频点信息
 */
static void wiota_remove_freq_from_list(uc_scan_freq_t *freq_info)
{
    freq_info_node_t *temp_node = wiota_freq_find_node(freq_info->freq_idx);
    if (temp_node)
    {
        rt_list_remove(&temp_node->node);
        rt_free(temp_node);
        temp_node = RT_NULL;
    }
}

/**
 * @brief 清除频点管理链表中的所有节点，释放所有节点资源
 *
 */
static void wiota_clean_freq_list(void)
{
    rt_list_t *next_node = g_freq_mng_info.freq_list.node.next;

    while (next_node != &g_freq_mng_info.freq_list.node)
    {
        freq_info_node_t *temp_node = rt_list_entry(next_node, freq_info_node_t, node);
        rt_free(temp_node);
        temp_node = RT_NULL;
        next_node = next_node->next;
    }
    wiota_freq_list_init();
}

/**
 * @brief 根据扫频结果选择频点管理链表中的第一个节点作为WIoTa的频点
 *
 * @return 0表示成功， 非0表示失败
 */
static int wiota_choose_freq(void)
{
    rt_list_t *head_node = &(g_freq_mng_info.freq_list.node);
    rt_list_t *next_node = head_node->next;
    wiota_freq_cfg_t *freq_cfg = uc_static_get_wiota_freq_cfg();

    if (next_node == head_node)
    {
        TRACE_W("g_freq_mng_info.freq_list is null");
        return 1;
    }

    if (g_freq_mng_info.cur_freq_node == RT_NULL)
    {
        g_freq_mng_info.cur_freq_node = rt_list_entry(next_node, freq_info_node_t, node);
    }
    else
    {
        next_node = g_freq_mng_info.cur_freq_node->node.next;
        if (next_node == head_node)
        {
            g_freq_mng_info.cur_freq_node = RT_NULL;
            return 2;
        }
        g_freq_mng_info.cur_freq_node = rt_list_entry(next_node, freq_info_node_t, node);
    }

    freq_cfg->current_freq = g_freq_mng_info.cur_freq_node->freq_info.freq_idx;
    TRACE_I("wiota_choose_freq %d", freq_cfg->current_freq);

    return 0;
}

/**
 * @brief 当静态数据频点列表中只有一个频点时，直接将该频点添加到频点管理链表中，无需扫频操作
 *
 * @param freq_idx 频点
 */
static void wiota_only_one_freq_set(unsigned char freq_idx)
{
    uc_scan_freq_t freq_list = {0};

    freq_list.freq_idx = freq_idx;
    wiota_add_freq_to_list(&freq_list);
    TRACE_I("add freq_idx %d to mng_list", freq_idx);
    wiota_choose_freq();
}

/**
 * @brief 处理扫频结果，将is_synced为0的频点加入频点管理链表中，将is_synced为1的从频点管理链表中移除
 *
 * @param scan_recv 扫频结果
 * @return 成功返回0，非0表示失败
 */
static int wiota_handle_scan_freq_result(uc_scan_recv_t scan_recv)
{
    uc_scan_freq_t *freq_info = NULL;
    int res = 1;

    if (UC_OP_SUCC == scan_recv.result && RT_NULL != scan_recv.freq_info)
    {
        freq_info = scan_recv.freq_info;

        for (int i = 0; i < scan_recv.freq_num; i++)
        {
            TRACE_I("freq_idx %d, snr %d, rssi %d, is_synced %d", freq_info->freq_idx, freq_info->snr, freq_info->rssi, freq_info->is_synced);
            if (freq_info->is_synced == 0)
            {
                wiota_add_freq_to_list(freq_info);
                res = 0;
            }
            else
            {
                wiota_remove_freq_from_list(freq_info);
            }
            freq_info++;
        }

        // 输出频点管理链表中的所有频点信息
        // wiota_print_freq_list();

        // 处理完成后，该指针需要手动释放
        rt_free(scan_recv.freq_info);
    }

    return res;
}

/**
 * @brief 扫频处理，扫频时需要设置系统配置，并启动WIoTa协议栈，扫频结束后退出协议栈
 *
 * @return 0表示成功，非0表示失败
 */
static int wiota_scan_freq_list(unsigned char *freq_list, unsigned char freq_num)
{
    uc_scan_recv_t scan_recv = {0};
    sub_system_config_t config = {0};
    wiota_sys_cfg_t *sys_cfg = RT_NULL;
    int res = 1;

    // 初始化WIoTa
    if (0 != uc_wiota_init())
    {
        TRACE_E("uc_wiota_init error");
        return 1;
    }

    // WioTa子网配置，扫频时配置主要是子网id（subsystem_id）不同，扫频结果会不同
    uc_wiota_get_system_config(&config);
    sys_cfg = uc_static_get_wiota_sys_cfg();

    config.symbol_length = sys_cfg->symbol_length;
    config.dlul_ratio = sys_cfg->dlul_ratio;
    config.bt_value = sys_cfg->bt_value;
    config.group_number = sys_cfg->group_number;
    config.subsystem_id = sys_cfg->subsystem_id;

    uc_wiota_set_system_config(&config);

    // 启动WIoTa
    if (0 != uc_wiota_run())
    {
        TRACE_E("uc_wiota_run error");
        uc_wiota_exit();
        return 2;
    }

    // 扫频可能失败，在扫之前先清下频点管理链表（正常情况下不会失败，如果失败必然存在某种异常，如AT通讯异常或AP8288启动异常）
    wiota_clean_freq_list();

    // 大于一个频点时扫频，当频点个数为0时表示全扫（共计201个频点），全扫是需要更长的时间，建议死等，如果很久未返回结果则可能异常，建议重启网关排查
    if (UC_OP_SUCC == uc_wiota_scan_freq(freq_list, freq_num, 0, freq_num == 0 ? -1 : WIOTA_SCAN_TIMEOUT, RT_NULL, &scan_recv))
    {
        // 扫频完成后处理扫频结果
        res = wiota_handle_scan_freq_result(scan_recv);
    }

    // 扫频完成后建议先退出WIoTa协议栈或直接重启，便于后面重设配置信息
    uc_wiota_exit();

    return res;
}

/**
 * @brief WIoTa扫频处理
 *
 */
void wiota_scan_freq(void)
{
    unsigned char freq_list[UC_FREQ_LIST_MAX_NUM] = {0};
    int freq_num = 0;

    // 频点管理信息初始化
    wiota_freq_list_init();

    // 从静态数据中获取频点列表
    freq_num = wiota_get_freq_list(freq_list);
    if (freq_num == 1)
    {
        // 只有一个频点,直接设置，无需进行扫频流程
        wiota_only_one_freq_set(freq_list[0]);
        return;
    }

    while (1)
    {
        if (g_freq_mng_info.continue_scan_fail >= MAX_SACN_FAIL_CNT)
        {
            TRACE_E("too many continue fail in scan freq, restart gateway");
            rt_thread_mdelay(100);
            watchdog_reboot();
        }
        // 扫频，失败后继续，直到成功
        // Note：是否添加异常情况处理，比如一直失败
        if (0 != wiota_scan_freq_list(freq_list, freq_num))
        {
            TRACE_E("wiota_scan_freq fail");
            g_freq_mng_info.continue_scan_fail++;
            rt_thread_mdelay(1000 * g_freq_mng_info.continue_scan_fail);
            continue;
        }

        g_freq_mng_info.continue_scan_fail = 0;
        TRACE_I("wiota_scan_freq ok");

        // 选择一个信号最好的频点作为WIoTa的频点
        wiota_choose_freq();
        break;
    }
}
